package com.cloud.tsp.service.impl;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cloud.api.constants.StrVo;
import com.cloud.api.domain.algorithm.Aggregation;
import com.cloud.api.domain.algorithm.VehiclePoint;
import com.cloud.api.domain.custom.*;
import com.cloud.api.domain.job.Addr;
import com.cloud.api.domain.report.VehicleOffline;
import com.cloud.api.domain.stats.OfflineVehicleVo;
import com.cloud.api.domain.stats.VehicleStats;
import com.cloud.api.domain.sys.DictTypeQuery;
import com.cloud.api.domain.tsp.Properties;
import com.cloud.api.domain.tsp.*;
import com.cloud.api.myenum.VinYear;
import com.cloud.api.utils.ExcelUtils;
import com.cloud.core.exception.MyException;
import com.cloud.mybatis.domain.PageModelNew;
import com.cloud.mybatis.domain.TableDataInfo;
import com.cloud.tsp.config.DbscanConfig;
import com.cloud.tsp.config.MysqlConfig;
import com.cloud.tsp.config.ResolveAddress;
import com.cloud.tsp.mapper.VehicleMessageDailyMapper;
import com.cloud.tsp.service.EmailService;
import com.cloud.tsp.service.IVehicleMessageDailyService;
import com.cloud.tsp.service.IVehicleSaleService;
import com.google.common.util.concurrent.RateLimiter;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.util.StringUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
import java.util.*;
/**
 * <p>
 * OLAP 服务实现类
 * </p>
 *
 * @author haohaibo
 * @since 2023-12-01
 */
@Service("vehicleMessageDailyService")
@Slf4j
@EnableAsync
public class VehicleMessageDailyServiceImpl extends ServiceImpl<VehicleMessageDailyMapper, VehicleMessageDaily> implements IVehicleMessageDailyService {

    @Resource
    private VehicleMessageDailyMapper vehicleMessageDailyMapper;
    @Resource
    private MessageSource messageSource;
    @Autowired
    private MysqlConfig mysqlConfig;
    @Resource(name = "myRedis")
    private RedisTemplate<String, Object> redisTemplate;

    @Resource
    private DbscanConfig dbscanConfig;

    @Resource
    private EmailService emailService;
    @Resource
    private ResolveAddress resolveAddress;

    @Resource
    private VinLiveServiceImpl vinLiveService;

    @Resource
    private IVehicleSaleService vehicleSaleService;

    private final RateLimiter rateLimiter = RateLimiter.create(1000.0); // 每秒最多1000次请求



    @Override
    public VehicleLocation selectVehicleLocation(String vin) {
        Locale locale = LocaleContextHolder.getLocale();
        VehicleLocation vehicleLocation = vehicleMessageDailyMapper.selectVehicleLocation(vin);
        if (vehicleLocation == null){
            return null;
        }
        Double timestamp = redisTemplate.opsForZSet().score("vehicle_state", vin);
        if (timestamp != null) {
            long between = ChronoUnit.SECONDS.between(LocalDateTime.ofInstant(Instant.ofEpochSecond(timestamp.longValue()), ZoneId.systemDefault()), LocalDateTime.now());
            if (between >= mysqlConfig.getSecond()) {
                vehicleLocation.setOnlineStatus(messageSource.getMessage("vehicle.status.offline", null, locale));
            }else {
                vehicleLocation.setOnlineStatus(messageSource.getMessage("vehicle.status.online", null, locale));
            }
        }else {
            vehicleLocation.setOnlineStatus(messageSource.getMessage("vehicle.status.offline", null, locale));
        }
        return vehicleLocation;
    }

    @Override
    public TableDataInfo<VinLockStatus> selectVinList(QueryVehicle para) {
        para.setDataSource(mysqlConfig.getDataSource());
        switch (para.getRange()){
            case "all" :
                return selectAllVinList(para);
            case "online" :
                return selectOnlineVinList(para);
            case "offline" :
                return selectOfflineVinList(para);
            default:
                throw new MyException("wds.exception.lineRange");
        }
    }

    @Override
    public TableDataInfo<VinLockStatus> selectVinListTsp(QueryVehicle para) {
        para.setDataSource(mysqlConfig.getDataSource());
        if (StringUtil.isBlank(para.getDeptId())){
            para.setDeptId(null);
        }
        switch (para.getRange()){
            case "all" :
                return selectAllVinListTsp(para);
            case "online" :
                return selectOnlineVinListTsp(para);
            case "offline" :
                return selectOfflineVinListTsp(para);
            default:
                throw new MyException("wds.exception.lineRange");
        }
    }

    @Override
    public VehicleStats selectOnlineVehicleCount() {
        VehicleStats vehicleStats = new VehicleStats();
        List<VehicleStats> list = vehicleMessageDailyMapper.selectOnlineVehicleCount(mysqlConfig.getDataSource());
        for (VehicleStats v: list ){
            if ("ev".equals(v.getType())){
                vehicleStats.setEvTotal(v.getCount());
            }else if ("oil".equals(v.getType())){
                vehicleStats.setOilTotal(v.getCount());
            }
        }
        vehicleStats.setTotal(vehicleStats.getEvTotal()+ vehicleStats.getOilTotal());
        return vehicleStats;
    }

    @Override
    public TableDataInfo<OfflineVehicleVo> selectOfflineVehicle(DictTypeQuery para) {
        para.setDataSource(mysqlConfig.getDataSource());
//        getPermission(para);
        Page<OfflineVehicleVo> page =new Page<>();
        page.setCurrent(para.getPage());
        page.setSize(para.getPageSize());
        IPage<OfflineVehicleVo> data = vehicleMessageDailyMapper.selectOfflineVehicle(page, para);
        desensitize(data.getRecords());
        return TableDataInfo.build(data);
    }

    private void desensitize(List<OfflineVehicleVo> data) {
        for (OfflineVehicleVo offlineVehicleVo : data){
            offlineVehicleVo.setVin(StrUtil.hide(offlineVehicleVo.getVin(), 8, 11));
            if ("oil".equals(offlineVehicleVo.getVehicleType())){
                offlineVehicleVo.setLicenseNumber(StrUtil.hide(offlineVehicleVo.getLicenseNumber(), 3, 6));
            }else {
                offlineVehicleVo.setLicenseNumber(StrUtil.hide(offlineVehicleVo.getLicenseNumber(), 4, 7));
            }
        }
    }

    @Override
    public void exportOfflineVehicle(DictTypeQuery para, HttpServletResponse response)throws IOException {
        para.setDataSource(mysqlConfig.getDataSource());
        Locale locale = LocaleContextHolder.getLocale();
        String fileName = "table.offlineVehicle";
        fileName = messageSource.getMessage(fileName, null, fileName, locale);

//        getPermission(para);
        List<OfflineVehicleVo> data = vehicleMessageDailyMapper.selectAllOfflineVehicle(para);
        desensitize(data);
        ExcelUtils.export(response,fileName,data,OfflineVehicleVo.class);
    }

    /*public void getPermission(DictTypeQuery para){
        para.setDataScope(LoginHelper.getDataScope());
        if (para.getDataScope() == 5){
            R<SystemRoleDto> roleDetail = systemRoleOpenFeignService.getById(new StrVo(LoginHelper.getUserLogin().getRoleId()));
            String[] split = roleDetail.getData().getDeptIds().split(",");
            List<String> deptIds = new ArrayList<>(Arrays.asList(split));
            para.setDeptIds(deptIds);
        }else {
            List<String> list = new ArrayList<>();
            list.add(LoginHelper.getUserLogin().getDeptId());
            para.setDeptIds(list);
        }
    }*/

    @Override
    public List<VehicleMap> getAllVehicleLocation(QueryVehicleMap para) {
        para.setDataSource(mysqlConfig.getDataSource());
        LocalDateTime now = LocalDateTime.now();
        //修改成30秒就离线
        LocalDateTime twoMinutesAgo = now.minusSeconds(mysqlConfig.getSecond());
        switch (para.getStatus()){
            case "on":
                para.setStartTime(twoMinutesAgo);
                para.setEndTime(now);
                break;
            case "off":
                para.setEndTime(twoMinutesAgo);
                break;
            default:
                break;
        }
        List<VinDetail> data = vehicleMessageDailyMapper.getAllVehicleLocation(para);
        List<VehicleMap> list = new ArrayList<>();

        for (VinDetail vinDetail : data){
            if (para.getStatus().equals("all")){
                long between = ChronoUnit.SECONDS.between(vinDetail.getOpTime(), LocalDateTime.now());
                if (between >= mysqlConfig.getSecond()) {
                    vinDetail.setStatus("off");
                }else {
                    vinDetail.setStatus("on");
                }
            }else if (para.getStatus().equals("off")){
                vinDetail.setStatus("off");
            }else {
                vinDetail.setStatus("on");
            }
            Properties properties = new Properties();
            properties.setDetail(vinDetail);
            properties.setAngle(0);
            properties.setAnchor(0);
            properties.setShapeType(4);

            Geometry geometry = new Geometry();
            geometry.setType("Point");
            geometry.setCoordinates(Arrays.asList(vinDetail.getLng(),vinDetail.getLat()));

            VehicleMap vehicleMap = new VehicleMap();
            vehicleMap.setType("Feature");
            vehicleMap.setProperties(properties);
            vehicleMap.setGeometry(geometry);
            list.add(vehicleMap);
        }
        return list;
    }

    @Override
    public TableDataInfo<VehicleMap> getAllVehicleLocationPage(PageModelNew pageModel) {
        Page<VehicleMap> page = new Page<>(pageModel.getPage(), pageModel.getPageSize());
        IPage<VinDetail> data = vehicleMessageDailyMapper.getAllVehicleLocationPage(page, mysqlConfig.getDataSource());
        Map<Object, Object> deptName = redisTemplate.opsForHash().entries("DEPT-NAME");

        List<VehicleMap> list = new ArrayList<>();

        for (VinDetail vinDetail : data.getRecords()){
            long between = ChronoUnit.SECONDS.between(vinDetail.getOpTime(), LocalDateTime.now());
            if (between >= mysqlConfig.getSecond()) {
                vinDetail.setStatus("off");
            }else {
                vinDetail.setStatus("on");
            }

            vinDetail.setDept(String.valueOf(deptName.get(vinDetail.getDept())));

            Properties properties = new Properties();
            properties.setDetail(vinDetail);
            properties.setAngle(0);
            properties.setAnchor(0);
            properties.setShapeType(4);

            Geometry geometry = new Geometry();
            geometry.setType("Point");
            geometry.setCoordinates(Arrays.asList(vinDetail.getLng(),vinDetail.getLat()));

            VehicleMap vehicleMap = new VehicleMap();
            vehicleMap.setType("Feature");
            vehicleMap.setProperties(properties);
            vehicleMap.setGeometry(geometry);
            list.add(vehicleMap);
        }
        TableDataInfo<VehicleMap> rspData = new TableDataInfo();
        rspData.setCode(200);
        rspData.setMsg("查询成功");
        rspData.setRows(list);
        rspData.setTotal(data.getTotal());
        return rspData;
    }

    //获取新能源车和油车的在线数
    @Override
    public List<VehicleStats> getOnlineVehicleTypeCount() {
        return vehicleMessageDailyMapper.getOnlineVehicleTypeCount(mysqlConfig.getDataSource(),mysqlConfig.getSecond());
    }

    //获取各种车型的在线数
    @Override
    public List<VehicleSale> getOnlineVehicleModelCount() {
        return vehicleMessageDailyMapper.getOnlineVehicleModelCount(mysqlConfig.getDataSource());
    }

    @Override
    public Integer selectOnlineCountTotal() {
        return vehicleMessageDailyMapper.selectOnlineCountTotal(mysqlConfig.getDataSource(),mysqlConfig.getSecond());
    }

    //今日上线数
    @Override
    public Integer getOnlineToday() {
        return vehicleMessageDailyMapper.getOnlineToday(mysqlConfig.getDataSource());
    }

    @Override
    public void exportOfflineList(HttpServletResponse response) {
        Locale locale = LocaleContextHolder.getLocale();
        String fileName = "table.vehicle.offline";
        List<VehicleOffline> vehicleOfflineList = vehicleMessageDailyMapper.selectOfflineList(mysqlConfig.getDataSource());
        vehicleOfflineList.forEach(vehicleOffline -> {
            String productionDate = vehicleOffline.getProductionDate();
            if (productionDate != null && productionDate.length() == 1) {
                char vinYearChar = productionDate.charAt(0);
                String yearOrChar = VinYear.fromChar(vinYearChar);
                vehicleOffline.setProductionDate(yearOrChar);
            }
        });
        fileName = messageSource.getMessage(fileName, null, fileName, locale);
        ExcelUtils.export(response,fileName,vehicleOfflineList, VehicleOffline.class);
    }
    @Override
    public void exportMailOfflineList() {
        Locale locale = LocaleContextHolder.getLocale();
        String fileName = "table.vehicle.offline";
        List<VehicleOffline> vehicleOfflineList = vehicleMessageDailyMapper.selectOfflineList(mysqlConfig.getDataSource());
        vehicleOfflineList.forEach(vehicleOffline -> {
            String productionDate = vehicleOffline.getProductionDate();
            if (productionDate != null && productionDate.length() == 1) {
                char vinYearChar = productionDate.charAt(0);
                String yearOrChar = VinYear.fromChar(vinYearChar);
                vehicleOffline.setProductionDate(yearOrChar);
            }
        });
        fileName = messageSource.getMessage(fileName, null, fileName, locale);

        try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
            // 导出Excel文件到ByteArrayOutputStream
            ExcelUtils.export(outputStream, fileName, vehicleOfflineList, VehicleOffline.class);
            // 获取字节数组副本，用于后续邮件发送
            byte[] fileBytes = outputStream.toByteArray();
            // 异步发送邮件
            emailService.sendEmailAsync("离线车辆数据导出", "请查看附件中的离线车辆数据。", fileName + ".xlsx", fileBytes);
        } catch (IOException e) {
            log.error("导出Excel文件失败", e);
        }
    }

    @Override
    public void exportMailAggVehicleMessage() {
        Locale locale = LocaleContextHolder.getLocale();
        String fileName = "table.vehicle.aggregation";
        List <VehiclePoint> points = vehicleMessageDailyMapper.getAggVehicleMessage(dbscanConfig.getParkTime(), mysqlConfig.getDataSource());
        Aggregation aggregation = new Aggregation();
        List<VehiclePoint> pointList = aggregation.directDistance(points);
        for (VehiclePoint point : pointList) {
            String lat = String.valueOf(point.getLat());
            String lng = String.valueOf(point.getLng());
            Addr addr = resolveAddress.getAddr(lat, lng);
            point.setAddress(addr.getAddress());
            point.setProvinceCity(addr.getProvince() + addr.getCity());
        }
        fileName = messageSource.getMessage(fileName, null, fileName, locale);

        try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
            // 导出Excel文件到ByteArrayOutputStream
            ExcelUtils.export(outputStream, fileName, pointList, VehiclePoint.class);
            // 获取字节数组副本，用于后续邮件发送
            byte[] fileBytes = outputStream.toByteArray();
            // 异步发送邮件
            emailService.sendEmailAsync("车辆各省市运行情况导出", "请查看附件中的车辆各省市运行情况。", fileName + ".xlsx", fileBytes);
        } catch (IOException e) {
            log.error("导出Excel文件失败", e);
        }
    }


    @Override
    public TableDataInfo<VehicleOffline> selectOfflineList(PageModelNew para) {
        Page<VehicleOffline> page =new Page<>();
        page.setCurrent(para.getPage());
        page.setSize(para.getPageSize());
        IPage<VehicleOffline> data = vehicleMessageDailyMapper.selectOffList(page,mysqlConfig.getDataSource());
        for (VehicleOffline vehicleOffline : data.getRecords()) {
            String productionDate = vehicleOffline.getProductionDate();
            if (productionDate != null && productionDate.length() == 1) {
                char vinYearChar = productionDate.charAt(0);
                String yearOrChar = VinYear.fromChar(vinYearChar);
                vehicleOffline.setProductionDate(yearOrChar);
            }
        }
        return TableDataInfo.build(data);
    }

    @Override
    public TableDataInfo<VehiclePoint> getAggVehicleMessage(PageModelNew para) {
        int page = Math.toIntExact(para.getPage());
        int pageSize = Math.toIntExact(para.getPageSize());
        List<VehiclePoint> points = vehicleMessageDailyMapper.getAggVehicleMessage(dbscanConfig.getParkTime(), mysqlConfig.getDataSource());
        Aggregation aggregation = new Aggregation();
        List<VehiclePoint> pointList = aggregation.directDistance(points);
//        for (VehiclePoint point : pointList) {
//            String productionDate = point.getProductionDate();
//            if (productionDate != null && productionDate.length() == 1) {
//                char vinYearChar = productionDate.charAt(0);
//                String yearOrChar = VinYear.fromChar(vinYearChar);
//                point.setProductionDate(yearOrChar);
//            }
//            String lat = String.valueOf(point.getLat());
//            String lng = String.valueOf(point.getLng());
//            rateLimiter.acquire(); // 限流 ，百度 1000 并发(次/秒)
//            Addr addr = resolveAddress.getAddr(lat, lng);
//            if(addr != null){
//                point.setAddress(addr.getAddress());
//                point.setProvinceCity(addr.getProvince() + addr.getCity());
//            }
//        }
        ArrayList<VehiclePoint> arrayList = new ArrayList<>();
        for (int i = (page-1) * pageSize ; i < page * pageSize && i < pointList.size(); i++) {

            VehiclePoint point = pointList.get(i);
            String productionDate = point.getProductionDate();
            if (productionDate != null && productionDate.length() == 1) {
                char vinYearChar = productionDate.charAt(0);
                String yearOrChar = VinYear.fromChar(vinYearChar);
                point.setProductionDate(yearOrChar);
            }
            String lat = String.valueOf(point.getLat());
            String lng = String.valueOf(point.getLng());
            rateLimiter.acquire(); // 限流 ，百度 1000 并发(次/秒)
            Addr addr = resolveAddress.getAddr(lat, lng);
            if(addr != null){
                point.setAddress(addr.getAddress());
                point.setProvinceCity(addr.getProvince() + addr.getCity());
            }
            arrayList.add(point);
        }
        TableDataInfo<VehiclePoint> build = TableDataInfo.build(arrayList);
        build.setTotal(pointList.size());
        return build;
    }

    @Override
    public void exportAggVehicleMessage(HttpServletResponse response) {
        Locale locale = LocaleContextHolder.getLocale();
        String fileName = "table.vehicle.aggregation";
        List <VehiclePoint> points = vehicleMessageDailyMapper.getAggVehicleMessage(dbscanConfig.getParkTime(), mysqlConfig.getDataSource());
        Aggregation aggregation = new Aggregation();
        List<VehiclePoint> pointList = aggregation.directDistance(points);
        for (VehiclePoint point : pointList) {
            String productionDate = point.getProductionDate();
            if (productionDate != null && productionDate.length() == 1) {
                char vinYearChar = productionDate.charAt(0);
                String yearOrChar = VinYear.fromChar(vinYearChar);
                point.setProductionDate(yearOrChar);
            }
            String lat = String.valueOf(point.getLat());
            String lng = String.valueOf(point.getLng());
            Addr addr = resolveAddress.getAddr(lat, lng);
            if(addr != null){
                point.setAddress(addr.getAddress());
                point.setProvinceCity(addr.getProvince() + addr.getCity());
            }
        }
        fileName = messageSource.getMessage(fileName, null, fileName, locale);
        ExcelUtils.export(response,fileName,pointList, VehiclePoint.class);
    }


    public TableDataInfo<VinLockStatus> selectAllVinList(QueryVehicle para) {
        Page<VinLockStatus> page =new Page<>();
        page.setCurrent(para.getPage());
        page.setSize(para.getPageSize());

//        QueryVehicle permissions = getPermissions(para);
//        IPage<VinLockStatus> data = vehicleMessageDailyMapper.selectVinList(page, permissions);
        IPage<VinLockStatus> data = vehicleMessageDailyMapper.selectVinList(page, para);
        return TableDataInfo.build(data);
    }

    public TableDataInfo<VinLockStatus> selectAllVinListTsp(QueryVehicle para) {
        Page<VinLockStatus> page =new Page<>();
        page.setCurrent(para.getPage());
        page.setSize(para.getPageSize());

//        QueryVehicle permissions = getPermissions(para);
//        IPage<VinLockStatus> data = vehicleMessageDailyMapper.selectAllVinListTsp(page, permissions);
        IPage<VinLockStatus> data = vehicleMessageDailyMapper.selectAllVinListTsp(page, para);
        return TableDataInfo.build(data);
    }

    /*public QueryVehicle getPermissions(QueryVehicle para){
        List<String> redisKeys = new ArrayList<>();
        String redisKey = "vehicle_";
        para.setDataScope(LoginHelper.getDataScope());
        String userDept = LoginHelper.getUserLogin().getDeptId();
        if (para.getDataScope() == 5){
            R<SystemRoleDto> roleDetail = systemRoleOpenFeignService.getById(new StrVo(LoginHelper.getUserLogin().getRoleId()));
            String[] split = roleDetail.getData().getDeptIds().split(",");
            List<String> deptIds = new ArrayList<>(Arrays.asList(split));
            para.setDeptIds(deptIds);
            for (String deptId : deptIds){
                String key = redisKey + deptId +"_"+ para.getDataScope();
                redisKeys.add(key);
            }
        }else if (para.getDataScope() == 1){
            List<String> list = new ArrayList<>();
            list.add(userDept);
            para.setDeptIds(list);
            redisKey = redisKey + "null_1";
            redisKeys.add(redisKey);
        }else {
            List<String> list = new ArrayList<>();
            list.add(userDept);
            para.setDeptIds(list);
            redisKey = redisKey + userDept +"_"+ para.getDataScope();
            redisKeys.add(redisKey);
        }
        para.setRedisKeys(redisKeys);
        return para;
    }*/


    public TableDataInfo<VinLockStatus> selectOnlineVinList(QueryVehicle para) {
        /*//从redis取的方案
        Map<String, Object> map = getFromRedis(para);
        int count = (int) map.get("count");
        int listCount = (int) map.get("listCount");
        List<String> list = (List<String>) map.get("list");
        if (listCount > 0){
            List<VinLockStatus> data = vehicleMessageDailyMapper.selectStatusByVin(list,null);
            IPage<VinLockStatus> statusData = new Page<>();
            statusData.setRecords(data).setTotal(count);
            return TableDataInfo.build(statusData);
        }*/
        //从数据库取的方案
        Page<VinLockStatus> page =new Page<>();
        page.setCurrent(para.getPage());
        page.setSize(para.getPageSize());
//        QueryVehicle permissions = getPermissions(para);
        QueryVehicle permissions = new QueryVehicle();
        LocalDateTime now = LocalDateTime.now();
        permissions.setStartTime(now.minus(mysqlConfig.getSecond(), ChronoUnit.SECONDS));
        permissions.setEndTime(now);
        IPage<VinLockStatus> data = vehicleMessageDailyMapper.selectLineVinList(page,permissions);
        return TableDataInfo.build(data);
    }

    public TableDataInfo<VinLockStatus> selectOnlineVinListTsp(QueryVehicle para) {
        //从数据库取的方案
        Page<VinLockStatus> page =new Page<>();
        page.setCurrent(para.getPage());
        page.setSize(para.getPageSize());
//        QueryVehicle permissions = getPermissions(para);

        QueryVehicle permissions = new QueryVehicle();
        LocalDateTime now = LocalDateTime.now();
        permissions.setStartTime(now.minus(mysqlConfig.getSecond(), ChronoUnit.SECONDS));
        permissions.setEndTime(now);
        permissions.setDataSource(para.getDataSource());
        IPage<VinLockStatus> data = vehicleMessageDailyMapper.selectLineVinListTsp(page,permissions);
        return TableDataInfo.build(data);
    }

    public TableDataInfo<VinLockStatus> selectOfflineVinList(QueryVehicle para) {
        /*//从redis取的方案
        Map<String, Object> map = getFromRedis(para);
        int count = (int) map.get("count");
        int listCount = (int) map.get("listCount");
        List<String> list = (List<String>) map.get("list");
        if (listCount > 0){
            List<VinLockStatus> data = vehicleMessageDailyMapper.selectStatusByVin(list,null);
            IPage<VinLockStatus> statusData = new Page<>();
            statusData.setRecords(data).setTotal(count);
            return TableDataInfo.build(statusData);
        }*/
        //从数据库取的方案
        Page<VinLockStatus> page =new Page<>();
        page.setCurrent(para.getPage());
        page.setSize(para.getPageSize());
//        QueryVehicle permissions = getPermissions(para);
        QueryVehicle permissions = new QueryVehicle();
        LocalDateTime now = LocalDateTime.now();
        permissions.setEndTime(now.minus(mysqlConfig.getSecond(), ChronoUnit.SECONDS));
        IPage<VinLockStatus> data = vehicleMessageDailyMapper.selectLineVinList(page,permissions);
        return TableDataInfo.build(data);
    }

    public TableDataInfo<VinLockStatus> selectOfflineVinListTsp(QueryVehicle para) {
        //从数据库取的方案
        Page<VinLockStatus> page =new Page<>();
        page.setCurrent(para.getPage());
        page.setSize(para.getPageSize());

//        QueryVehicle permissions = getPermissions(para);
        QueryVehicle permissions = new QueryVehicle();
        LocalDateTime now = LocalDateTime.now();
        permissions.setEndTime(now.minus(mysqlConfig.getSecond(), ChronoUnit.SECONDS));
        permissions.setDataSource(para.getDataSource());
        IPage<VinLockStatus> data = vehicleMessageDailyMapper.selectLineVinListTsp(page,permissions);
        return TableDataInfo.build(data);
    }

    /*public Map<String,Object> getFromRedis(QueryVehicle para){
        Map<String, Object> conditions = getConditions(para);
        long startScore = (long) conditions.get("startScore");
        long endScore = (long) conditions.get("endScore");
        int start = (int) conditions.get("start");
        int end = (int) conditions.get("end");
        //返回前端的总数
        int count = 0;
        RBatch batch = RedisUtils.getClient().createBatch();
        //去除模糊查询
        if (StringUtil.isNotBlank(para.getVin())){
            count = RedisUtils.getClient().getScoredSortedSet("vehicle_state").rangeTo(para.getVin(),startScore, true, endScore, true);
            batch.getScoredSortedSet("vehicle_state").revRangeToAsync(para.getVin(),startScore,true,endScore,true,start,end);
        }else {
            count = RedisUtils.getClient().getScoredSortedSet("vehicle_state").count(startScore, true, endScore, true);
            batch.getScoredSortedSet("vehicle_state").valueRangeReversedAsync(startScore,true,endScore,true,start,end);
        }
        QueryVehicle permissions = getPermissions(para);
        List<String> redisKeys = permissions.getRedisKeys();
        if (redisKeys.size() >1){
            batch.getSet("union").unionAsync(redisKeys.toArray(new String[0]));
            batch.getScoredSortedSet("result").intersectionAsync("vehicle_state","union");
        }else {
            batch.getScoredSortedSet("result").intersectionAsync("vehicle_state",redisKeys.get(0));
        }
        count = RedisUtils.getClient().getScoredSortedSet("result").count(startScore, true, endScore, true);
        batch.getScoredSortedSet("result").valueRangeReversedAsync(startScore,true,endScore,true,start,end);
        BatchResult<?> execute = batch.execute();
        List<?> responses = execute.getResponses();
        Map<String, Object> map = HandleResult(responses);
        map.put("count",count);
        return map;
    }*/

    public Map<String,Object> HandleResult(List<?> responses){
        int listCount = 0;
        List<String>list = new ArrayList<>();
        Integer count = (Integer) responses.get(responses.size()-2);
        if (count > 0){
            list = new ArrayList<>((LinkedHashSet<String>) responses.get(responses.size()-1));
            listCount = list.size();
        }
        /*if (responses.get(0) instanceof LinkedHashSet){
            list = new ArrayList<>((LinkedHashSet<String>) responses.get(1));
            listCount = list.size();
        }else {
            listCount = (Integer) responses.get(0);
        }*/
        Map<String,Object> map = new HashMap<>();

        map.put("listCount",listCount);
        map.put("list",list);
        return map;
    }

    public Map<String,Object> getConditions(QueryVehicle para){
        long currentTimeMillis = System.currentTimeMillis();
        long twoMinutesAgo = currentTimeMillis - 2 * 60 * 1000;
        long startScore = 0L;
        long endScore = 0L;
        if (para.getRange().equals("online")){
            startScore = twoMinutesAgo;
            endScore = currentTimeMillis;
        }else {
            endScore = twoMinutesAgo;
        }

        Long page = para.getPage();
        Long pageSize = para.getPageSize();
        int start = (int)((page - 1) * pageSize);
        int end = (int)(page * pageSize - 1);
        Map<String,Object> map = new HashMap<>();
        map.put("startScore",startScore);
        map.put("endScore",endScore);
        map.put("start",start);
        map.put("end",end);
        return map;
    }

    @Override
    public VehicleCustomStatus getCustomStatus(StrVo para) {

        String vin = vehicleSaleService.getById(para.getId()).getVin();
        VehicleCustomStatus customStatus = new VehicleCustomStatus();
        if (StringUtils.hasText(vin)) {
            LambdaQueryWrapper<VehicleMessageDaily> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(VehicleMessageDaily::getVin, vin);
            List<VehicleMessageDaily> vehicleMessageDailies = vehicleMessageDailyMapper.selectList(wrapper);

            if (!vehicleMessageDailies.isEmpty()) {
                VehicleMessageDaily vehicleMessageDaily = vehicleMessageDailies.getFirst();

                try {
                    customStatus.setVin(vin);

                    BeanUtils.copyProperties(vehicleMessageDaily, customStatus);
                    // 在 getCustomStatus 方法中
                    // 累计工作时间处理
                    Long accumulatedWorkingTime = vehicleMessageDaily.getAccumulatedWorkingTime();
                    if (accumulatedWorkingTime != null) {
                        if (accumulatedWorkingTime == 0xFFFFFFFFL) {
                            customStatus.setAccumulatedWorkingTime("无效");
                        } else {
                            customStatus.setAccumulatedWorkingTime(String.valueOf(accumulatedWorkingTime));
                        }
                    }

                    // 总容量处理
                    Integer totalStorageCapacity = vehicleMessageDaily.getTotalStorageCapacity();
                    if (totalStorageCapacity != null) {
                        if (totalStorageCapacity == 0xFFFF) {
                            customStatus.setTotalStorageCapacity("无效");
                        } else {
                            customStatus.setTotalStorageCapacity(String.valueOf(totalStorageCapacity));
                        }
                    }

                    // 剩余容量处理
                    Integer remainingStorageCapacity = vehicleMessageDaily.getRemainingStorageCapacity();
                    if (remainingStorageCapacity != null) {
                        if (remainingStorageCapacity == 0xFFFF) {
                            customStatus.setRemainingStorageCapacity("无效");
                        } else {
                            customStatus.setRemainingStorageCapacity(String.valueOf(remainingStorageCapacity));
                        }
                    } // 否则保持 null


                    Short gpsSignalStrength = vehicleMessageDaily.getGpsSignalStrength();
                    if (gpsSignalStrength != null) {
                        if (gpsSignalStrength >= 1 && gpsSignalStrength <= 5) {
                            customStatus.setGpsSignalStrength(gpsSignalStrength.toString());
                        } else {
                            customStatus.setGpsSignalStrength("未知");
                        }
                    }
                    Short gsmSignalStrength = vehicleMessageDaily.getGsmSignalStrength();
                    if (gsmSignalStrength != null) {
                        if (gsmSignalStrength >= 5 && gsmSignalStrength <= 31) {
                            customStatus.setGsmSignalStrength(gsmSignalStrength.toString());
                        } else {
                            customStatus.setGsmSignalStrength("未知");
                        }
                    }
                    customStatus.setLockStatus(LockStatusEnum.fromValue(vehicleMessageDaily.getLockStatus()));
                    customStatus.setCanStatus(CanStatusEnum.fromValue(vehicleMessageDaily.getCanStatus()));
                    customStatus.setAntennaStatus(AntennaStatusEnum.fromValue(vehicleMessageDaily.getAntennaStatus()));
                    customStatus.setPowerOffStatus(PowerOffStatusEnum.fromValue(vehicleMessageDaily.getPowerOffStatus()));
                    customStatus.setSdCardStatus(SdCardStatusEnum.fromValue(vehicleMessageDaily.getSdCardStatus()));
                    customStatus.setLockEnableStatus(LockEnableStatusEnum.fromValue(vehicleMessageDaily.getLockEnableStatus()));
                    customStatus.setKeyLockStatus(KeyLockStatusEnum.fromValue(vehicleMessageDaily.getKeyLockStatus()));
                } catch (Exception e) {
                    log.error("获取车辆当前自定义状态失败 ", e);
                   throw  new MyException("获取车辆当前自定义状态");
                }

            }
        }
        return customStatus;
    }

    @Override
    public Boolean isOnline(QueryVehicleMap para) {
        return vehicleMessageDailyMapper.isOnline(para);
    }
}